// $Id: buffer.c 42 2011-04-15 16:06:04Z cedric.shih@gmail.com $
/*
 * Copyright (C) 2010 Cedric Shih <cedric.shih@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <string.h>
#include <stdlib.h>
#include <string.h>

#include "nio.h"
#include "buffer.h"

#include "nio/test.h"

#define CAPACITY 32
static struct niobuffer *buffer;

static void test_setup(void)
{
	buffer = niobuffer_new(CAPACITY);
}

static void test_teardown(void)
{
	niobuffer_free(buffer);
}

static void test_init(void)
{
	TEST_ASSERT_NOT_NULL(buffer);
	TEST_ASSERT_EQUAL_INT(0, niobuffer_position(buffer));
	TEST_ASSERT_EQUAL_INT(CAPACITY, niobuffer_capacity(buffer));
	TEST_ASSERT_EQUAL_INT(CAPACITY, niobuffer_limit(buffer));
	TEST_ASSERT_EQUAL_INT(CAPACITY, niobuffer_remaining(buffer));
}

static void test_put(void)
{
	const char *str = "0123456789";
	int rc, i, len;

	len = strlen(str);

	for (i = 0; i < CAPACITY; i ++) {
		rc = niobuffer_put(buffer, str[i % len]);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(i + 1, niobuffer_position(buffer));
	}

	rc = niobuffer_put(buffer, 0);
	TEST_ASSERT_EQUAL_INT(NIO_EOVERFLOW, rc);
	TEST_ASSERT_EQUAL_INT(i, niobuffer_position(buffer));
}

static void test_puts(void)
{
	const char *str = "0123456789";
	int rc, i, len, loop;

	len = strlen(str);
	loop = CAPACITY / len;

	for (i = 0; i < loop; i ++) {
		rc = niobuffer_puts(buffer, (unsigned char *)str, len);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(len * (i + 1), niobuffer_position(buffer));
	}

	rc = niobuffer_puts(buffer, (unsigned char *)str, len);
	TEST_ASSERT_EQUAL_INT(NIO_EOVERFLOW, rc);
	TEST_ASSERT_EQUAL_INT(len * i, niobuffer_position(buffer));
}

static void test_putf(void)
{
	const char *str = "0123456789";
	int rc, i, len, loop;

	len = strlen(str);
	loop = CAPACITY / len;

	for (i = 0; i < loop; i ++) {
		rc = niobuffer_putf(buffer, "%s", str);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(len * (i + 1), niobuffer_position(buffer));
	}

	rc = niobuffer_putf(buffer, "%s", str);
	TEST_ASSERT_EQUAL_INT(NIO_EOVERFLOW, rc);
	TEST_ASSERT_EQUAL_INT(len * i, niobuffer_position(buffer));
}

static void test_rewind(void)
{
	const char *str = "0123456789";
	int rc, len;
	size_t pos, limit;

	len = strlen(str);

	rc = niobuffer_puts(buffer, (unsigned char *)str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	pos = niobuffer_position(buffer);
	TEST_ASSERT_EQUAL_INT(len, pos);

	limit = niobuffer_limit(buffer);
	niobuffer_rewind(buffer);
	TEST_ASSERT_EQUAL_INT(0, niobuffer_position(buffer));
	TEST_ASSERT_EQUAL_INT(limit, niobuffer_limit(buffer));
}

static void test_flip(void)
{
	const char *str = "0123456789";
	int rc, len;
	size_t pos;

	len = strlen(str);

	rc = niobuffer_puts(buffer, (unsigned char *)str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	pos = niobuffer_position(buffer);
	TEST_ASSERT_EQUAL_INT(len, pos);

	niobuffer_flip(buffer);
	TEST_ASSERT_EQUAL_INT(0, niobuffer_position(buffer));
	TEST_ASSERT_EQUAL_INT(pos, niobuffer_limit(buffer));
}

static void test_clear(void)
{
	const char *str = "0123456789";
	int rc, len;
	size_t pos;

	len = strlen(str);

	rc = niobuffer_puts(buffer, (unsigned char *)str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	pos = niobuffer_position(buffer);
	TEST_ASSERT_EQUAL_INT(len, pos);

	niobuffer_clear(buffer);
	TEST_ASSERT_EQUAL_INT(0, niobuffer_position(buffer));
	TEST_ASSERT_EQUAL_INT(CAPACITY, niobuffer_limit(buffer));
}

static void test_get(void)
{
	const char *str = "0123456789";
	int rc, i, len, b;

	len = strlen(str);

	rc = niobuffer_puts(buffer, (unsigned char *)str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT_EQUAL_INT(len, niobuffer_position(buffer));

	niobuffer_flip(buffer);

	for (i = 0; i < len; i ++) {
		rc = niobuffer_get(buffer, &b);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(b, str[i]);
	}

	rc = niobuffer_get(buffer, &b);
	TEST_ASSERT_EQUAL_INT(NIO_EUNDERFLOW, rc);
}

static void test_gets(void)
{
	const char *str = "0123456789";
	int rc, len;
	char temp[CAPACITY];

	len = strlen(str);

	rc = niobuffer_puts(buffer, (unsigned char *)str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT_EQUAL_INT(len, niobuffer_position(buffer));

	niobuffer_flip(buffer);

	rc = niobuffer_gets(buffer, (unsigned char *)temp, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT(!memcmp(str, temp, len));

	niobuffer_rewind(buffer);

	rc = niobuffer_gets(buffer, (unsigned char *)temp, CAPACITY);
	TEST_ASSERT_EQUAL_INT(NIO_EUNDERFLOW, rc);
}

static void test_resize(void)
{
	const char *str = "0123456789";
	int rc, len;
	char temp[CAPACITY];

	len = strlen(str);

	rc = niobuffer_puts(buffer, (unsigned char *)str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT_EQUAL_INT(len, niobuffer_position(buffer));

	niobuffer_flip(buffer);

	rc = niobuffer_resize(buffer, len - 1);
	TEST_ASSERT_EQUAL_INT(NIO_EUNDERFLOW, rc);

	rc = niobuffer_resize(buffer, len);
	TEST_ASSERT_EQUAL_INT(0, rc);

	rc = niobuffer_gets(buffer, (unsigned char *)temp, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT(!memcmp(str, temp, len));
}

static void test_skip(void)
{
	const char *str = "0123456789";
	int rc, i, len;

	len = strlen(str);

	rc = niobuffer_putf(buffer, str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT_EQUAL_INT(len, niobuffer_position(buffer));

	niobuffer_flip(buffer);

	for (i = 0; i < len; i ++) {
		TEST_ASSERT_EQUAL_INT(i, niobuffer_position(buffer));

		rc = niobuffer_skip(buffer, 1);
		TEST_ASSERT_EQUAL_INT(0, rc);
	}

	rc = niobuffer_skip(buffer, 1);
	TEST_ASSERT_EQUAL_INT(NIO_EUNDERFLOW, rc);
}

static void test_compact(void)
{
	const char *str = "0123456789";
	int rc, i, len, b;
	size_t remain;

	len = strlen(str);

	rc = niobuffer_putf(buffer, str, len);
	TEST_ASSERT_EQUAL_INT(0, rc);
	TEST_ASSERT_EQUAL_INT(len, niobuffer_position(buffer));

	niobuffer_flip(buffer);
	remain = niobuffer_remaining(buffer);
	TEST_ASSERT_EQUAL_INT(len, remain);

	for (i = 0; i < len; i ++) {
		TEST_ASSERT_EQUAL_INT(0, niobuffer_position(buffer));

		rc = niobuffer_get(buffer, &b);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(b, str[i]);

		remain = niobuffer_remaining(buffer);
		TEST_ASSERT_EQUAL_INT(len - i - 1, remain);

		rc = niobuffer_compact(buffer);
		TEST_ASSERT_EQUAL_INT(0, rc);
		TEST_ASSERT_EQUAL_INT(remain, niobuffer_position(buffer));
		TEST_ASSERT_EQUAL_INT(CAPACITY, niobuffer_limit(buffer));

		niobuffer_flip(buffer);
	}

	TEST_ASSERT_EQUAL_INT(0, niobuffer_position(buffer));
	TEST_ASSERT_EQUAL_INT(0, niobuffer_limit(buffer));
}

TestRef niobuffer_test(void)
{
	EMB_UNIT_TESTFIXTURES(fixtures) {
		new_TestFixture("niobuffer_test_init", test_init),
		new_TestFixture("niobuffer_test_put", test_put),
		new_TestFixture("niobuffer_test_puts", test_puts),
		new_TestFixture("niobuffer_test_putf", test_putf),
		new_TestFixture("niobuffer_test_rewind", test_rewind),
		new_TestFixture("niobuffer_test_flip", test_flip),
		new_TestFixture("niobuffer_test_clear", test_clear),
		new_TestFixture("niobuffer_test_get", test_get),
		new_TestFixture("niobuffer_test_gets", test_gets),
		new_TestFixture("niobuffer_test_resize", test_resize),
		new_TestFixture("niobuffer_test_skip", test_skip),
		new_TestFixture("niobuffer_test_compact", test_compact),
	};
	EMB_UNIT_TESTCALLER(test_case, "niobuffer_test_case", test_setup, test_teardown, fixtures);
	return (TestRef)&test_case;
}
